package br.com.zynger.mst.addressbook;

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.border.EmptyBorder;
import javax.swing.table.DefaultTableModel;

public class AddressBookGUI extends JFrame {
	
	private static final long serialVersionUID = 1L;
	private static AddressBookGUI frame;
	private JTable table;
	private DefaultTableModel tableModel;
	private static AddressBook addressBook;
	
	public static void main(String[] args) {
		addressBook = new AddressBook();
		
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					frame = new AddressBookGUI();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}
	
	/**
	 * Create the frame.
	 */
	public AddressBookGUI() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setSize(600, 400);
		setLocationRelativeTo(null);
		setTitle("Address Book");
		
		setMenus();
		
		JPanel contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		contentPane.setLayout(new BorderLayout(0, 0));
		setContentPane(contentPane);

		setTable();
		tableModel.addColumn("Name");
		tableModel.addColumn("Details");
		
		setButtonsPane();
	}

	/**
	 * Set the GUI components to add the
	 * menu bar to the application frame.
	 */
	private void setMenus() {
		JMenuBar menuBar = new JMenuBar();
		setJMenuBar(menuBar);
		
		JMenu menuSort = new JMenu("Sort");
		JMenuItem menuSearch = new JMenuItem("Search");
		menuBar.add(menuSort);
		menuBar.add(menuSearch);
		
		JMenuItem sortByName = new JMenuItem("By Name");
		JMenuItem sortByZip = new JMenuItem("By Zip Code");
		menuSort.add(sortByName);
		menuSort.add(sortByZip);
		
		menuSearch.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				showSearchForm();
			}
		});
		
		sortByName.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				if(tableModel.getRowCount() > 1){					
					sortByName();
					updateTableWithAddressBook();
				}
			}
		});
		
		sortByZip.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				if(tableModel.getRowCount() > 1){					
					sortByZipCode();
					updateTableWithAddressBook();
				}
			}
		});
	}
	
	/**
	 * Set the GUI components to add the
	 * scrollable table to the
	 * application window.
	 */
	private void setTable() {
		tableModel = new DefaultTableModel(){
			private static final long serialVersionUID = 1L;

			@Override
		    public boolean isCellEditable(int row, int column) {
		       // make cells non-editable
		       return false;
		    }
		};
		table = new JTable(tableModel);
		table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
 
		JScrollPane scroll = new JScrollPane();
		scroll.setViewportView(table);
		add(scroll);
		pack();
	}
	
	/**
	 * Set the GUI components to add the
	 * bottom button bar, and their functionalities
	 * (add, edit and remove).
	 */
	private void setButtonsPane() {
		JPanel buttonsPane = new JPanel();
		buttonsPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		buttonsPane.setLayout(new FlowLayout(FlowLayout.CENTER));
		getContentPane().add(buttonsPane, BorderLayout.SOUTH);
		
		JButton btAdd = new JButton("Add");
		JButton btEdit = new JButton("Edit");
		JButton btRemove = new JButton("Remove");
		
		buttonsPane.add(btAdd);
		buttonsPane.add(btEdit);
		buttonsPane.add(btRemove);
		
		btAdd.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				showPersonForm(null);
			}
		});
		
		btEdit.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				Person p = getPersonFromSelectedRow();
				if(null != p) showPersonForm(p);
			}
		});
		
		btRemove.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				Person p = getPersonFromSelectedRow();
				if(null != p){					
					removePerson(p.getFirstName(), p.getLastName());
					updateTableWithAddressBook();
				}
			}
		});
	}
	
	/**
	 * Return the person referenced by the row selected in the table
	 * at the moment.
	 *
	 * @return The person in the row, or <code>null</code> 
	 * if there is no selected row.
	 */
	private Person getPersonFromSelectedRow(){
		int selectedRow = table.getSelectedRow();
		if(selectedRow != -1){					
			String nameRaw = tableModel.getValueAt(selectedRow, 0).toString();
			String firstName = nameRaw.substring(nameRaw.indexOf(", ") + 2);
			String lastName = nameRaw.substring(0, nameRaw.indexOf(","));
			
			return searchPerson(firstName, lastName);
		}else return null;
	}
	
	/**
	 * Build the search frame for the user to
	 * be able to find a specific person
	 * in the address book's entries.
	 */
	private void showSearchForm(){
		//Refactoring 5: Extract class
		final SearchForm searchForm = new SearchForm();
		
		ActionListener confirmAction = new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				String firstName = searchForm.getFirstNameTextFieldValue();
				String lastName = searchForm.getLastNameTextFieldValue();
				
				if(!firstName.equals("") && !lastName.equals("")){					
					Person p = searchPerson(firstName, lastName);
					if(null != p) showPersonForm(p);
					else JOptionPane.showMessageDialog(new JFrame(),
							firstName + " " + lastName + " was not found in the address book.",
							"Error!", JOptionPane.ERROR_MESSAGE);

					searchForm.setVisible(false);
				}else{
					JOptionPane.showMessageDialog(new JFrame(),
							"You must fill both fields!",
							"Missing information", JOptionPane.INFORMATION_MESSAGE);
				}
				
			}
		};
				
		ActionListener cancelAction = new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				searchForm.setVisible(false);
			}
		};
		
		searchForm.setConfirmButtonAction(confirmAction);
		searchForm.setCancelButtonAction(cancelAction);
		searchForm.setVisible(true);
	}
	
	/**
	 * Build the add (or edit) frame for the user to
	 * be able to add/edit a specific person
	 * in/from the address book's entries.
	 * 
	 * @param person The person to be edited, or
	 * <code>null</code> if the frame is used
	 * to add a person to the address book.
	 */
	private void showPersonForm(final Person person){
		//Refactoring 5: Extract class
		final PersonForm personForm;
		if(null == person) personForm = new PersonForm();
		else personForm = new PersonForm(person);
		
		ActionListener confirmAction = new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				String firstName = personForm.getFirstNameTextFieldValue();
				String lastName = personForm.getLastNameTextFieldValue();
				String address = personForm.getAddressTextFieldValue();
				String city = personForm.getCityTextFieldValue();
				String state = personForm.getStateTextFieldValue();
				String phoneNumber = personForm.getPhoneNumberTextFieldValue();
				String zipCode = personForm.getZipCodeTextFieldValue();

				if(null == person) addPerson(firstName, lastName, address, city, state, phoneNumber, zipCode);
				else editPerson(firstName, lastName, address, city, state, phoneNumber, zipCode);
				
				updateTableWithAddressBook();
				personForm.setVisible(false);
			}
		};
		
		ActionListener cancelAction = new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				personForm.setVisible(false);
			}
		};
		
		personForm.setConfirmButtonAction(confirmAction);
		personForm.setCancelButtonAction(cancelAction);
		personForm.setVisible(true);
	}
	
	/**
	 * Reset the table and re-add the entries
	 * in the address book to the table.
	 */
	private void updateTableWithAddressBook(){
		tableModel.setRowCount(0); // Clear the table
		List<Person> entries = getAddressBook().getEntries();
		for (int i = 0; i < entries.size(); i++) {
			Person p = entries.get(i);
			String valueName = p.getLastName() + ", " + p.getFirstName();
			String valueDetails = p.getAddress() + " " + p.getCity() + " "
					+ p.getState() + " " + p.getZipCode() + " "
					+ p.getPhoneNumber();
			tableModel.addRow(new Object[]{valueName, valueDetails});
		}
	}
	
	private AddressBook getAddressBook() {
		return addressBook;
	}
	
	protected void addPerson(String firstName, String lastName, String address, String city, String state, String phoneNumber, String zipCode) {
		Person p = new Person(firstName, lastName, address, city, state, phoneNumber, zipCode);
		getAddressBook().addPerson(p);
	}
	
	protected Person searchPerson(String firstName, String lastName){
		return getAddressBook().getPerson(firstName, lastName);
	}
	
	protected void removePerson(String firstName, String lastName){
		getAddressBook().removePerson(searchPerson(firstName, lastName));
	}
	
	protected void editPerson(String firstName, String lastName, String address, String city, String state, String phoneNumber, String zipCode){
		Person p = new Person(firstName, lastName, address, city, state, phoneNumber, zipCode);
		getAddressBook().editPerson(p);
	}
	
	protected void sortByName(){
		getAddressBook().sortByName();
	}
	
	protected void sortByZipCode(){
		getAddressBook().sortByZipCode();
	}
}
